﻿using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;

namespace XKControlLib.Print
{
    public class CustomPaperSize
    {
        private const int DM_OUT_BUFFER = 2;
        private const int DM_IN_PROMPT = 4;
        private const int DM_IN_BUFFER = 8;

        [DllImport("winspool.Drv")]
        private static extern bool AddForm(IntPtr phPrinter, int level, ref StructFormInfo form);

        [DllImport("winspool.Drv")]
        private static extern bool ClosePrinter(IntPtr phPrinter);

        [DllImport("GDI32.dll")]
        private static extern IntPtr CreateDC(string pDrive, string pName, string pOutput, ref StructDevMode pDevMode);

        [DllImport("GDI32.dll")]
        private static extern bool DeleteDC(IntPtr hDC);

        [DllImport("winspool.Drv")]
        private static extern bool DeleteForm(IntPtr phPrinter, string pName);

        [DllImport("kernel32.dll")]
        private static extern int GetLastError();

        [DllImport("winspool.Drv")]
        private static extern bool OpenPrinter(string printerName, ref IntPtr phPrinter, ref StructPrinterDefaults pd);

        [DllImport("GDI32.dll")]
        private static extern IntPtr ResetDC(IntPtr hDC, ref StructDevMode pDevMode);

        [DllImport("winspool.Drv")]
        private static extern bool SetForm(IntPtr phPrinter, string pName, int level, ref StructFormInfo form);

        [DllImport("winspool.drv")]
        private static extern IntPtr PrinterProperties(IntPtr hwnd, IntPtr hPrinter);

        [DllImport("winspool.drv")]
        private static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);

        public static void ShowProperties(string printerName)
        {
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
            {
                var defaults = new StructPrinterDefaults
                {
                    pDatatype = null,
                    pDevMode = IntPtr.Zero,
                    DesiredAccess = 12
                };

                var hWnd = IntPtr.Zero;
                var hPrinter = IntPtr.Zero;
                if (OpenPrinter(printerName, ref hPrinter, ref defaults))
                {
                    try
                    {
                        var iNeeded = PrinterProperties(hWnd, hPrinter);
                    }
                    catch (Exception)
                    {
                        ClosePrinter(hPrinter);
                    }
                }

            }
        }

        public static IntPtr ShowPrinter(string printerName)
        {
            IntPtr hWnd = IntPtr.Zero;
            IntPtr hPrinter = IntPtr.Zero;
            IntPtr pDevMode = IntPtr.Zero;
            IntPtr pDevModeOutput = IntPtr.Zero;
            IntPtr pDevModeInput = IntPtr.Zero;
            int dwNeeded = 0;
            int dwRet = 0;
            dwNeeded = DocumentProperties(hWnd, hPrinter, printerName, pDevModeInput, pDevModeInput, 0);
            pDevMode = Marshal.AllocHGlobal(dwNeeded);
            dwRet = DocumentProperties(hWnd, hPrinter, printerName, pDevMode, pDevModeInput, DM_IN_BUFFER | DM_OUT_BUFFER | DM_IN_PROMPT);
            if (dwRet != 1)
            {
                Marshal.FreeHGlobal(pDevMode);
                ClosePrinter(hPrinter);
                return IntPtr.Zero;
            }
            dwRet = DocumentProperties(hWnd, hPrinter, printerName, pDevMode, pDevMode, DM_IN_BUFFER | DM_OUT_BUFFER);
            ClosePrinter(hPrinter);
            if (dwRet != 1)
            {
                Marshal.FreeHGlobal(pDevMode);
                return IntPtr.Zero;
            }
            return pDevMode;
        }

        public static PaperSize GetPrintForm(string printerName, string paperName, out short id)
        {
            short i = 0;
            id = -1;
            PrinterSettings printerSettings = new PrinterSettings { PrinterName = printerName };
            PaperSize ps = null;
            foreach (PaperSize ps_loopVariable in printerSettings.PaperSizes)
            {
                ps = ps_loopVariable;
                i = Convert.ToInt16((i + 1));
                if ((ps.PaperName.ToLower() == paperName.ToLower()))
                {
                    id = i;
                    return ps;
                }
            }
            return null;
        }

        public static void SetPrintForm(string printerName, string paperName, float width, float height)
        {
            if ((Environment.OSVersion.Platform == PlatformID.Win32NT))
            {
                StructPrinterDefaults defaults = new StructPrinterDefaults
                {
                    pDatatype = null,
                    pDevMode = IntPtr.Zero,
                    DesiredAccess = 12
                };
                IntPtr hPrinter = IntPtr.Zero;
                if (OpenPrinter(printerName, ref hPrinter, ref defaults))
                {
                    try
                    {
                        StructFormInfo formInfo = new StructFormInfo
                        {
                            Flags = 0,
                            pName = paperName
                        };
                        formInfo.Size.width = Convert.ToInt32((width * 100));
                        formInfo.Size.height = Convert.ToInt32((height * 100));
                        formInfo.ImageableArea.left = 0;
                        formInfo.ImageableArea.right = formInfo.Size.width;
                        formInfo.ImageableArea.top = 0;
                        formInfo.ImageableArea.bottom = formInfo.Size.height;
                        bool result = false;
                        short i = -1;
                        if (((GetPrintForm(printerName, paperName, out i) != null)))
                        {
                            result = SetForm(hPrinter, paperName, 1, ref formInfo);
                        }
                        else
                        {
                            AddCustomPaperSize(printerName, paperName, width, height);
                            result = true;
                        }
                        if (!result)
                        {
                            throw new Exception(string.Format("添加纸张{0}时发生错误，系统错误信息：{1}", paperName, GetLastError()));
                        }
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(string.Format("添加纸张{0}时发生错误", paperName, GetLastError()), ex);
                    }
                    finally
                    {
                        ClosePrinter(hPrinter);
                    }
                }
            }

        }

        public static void AddCustomPaperSize(string printerName, string paperName, float width, float height)
        {
            if ((Environment.OSVersion.Platform == PlatformID.Win32NT))
            {
                StructPrinterDefaults defaults = new StructPrinterDefaults
                {
                    pDatatype = null,
                    pDevMode = IntPtr.Zero,
                    DesiredAccess = 12
                };
                IntPtr hPrinter = IntPtr.Zero;
                if (!OpenPrinter(printerName, ref hPrinter, ref defaults))
                {
                    throw new Exception(string.Format("打开打印机{0}时发生错误，系统错误信息：{1}", printerName, GetLastError()));
                }
                try
                {
                    DeleteForm(hPrinter, paperName);
                    StructFormInfo formInfo = new StructFormInfo
                    {
                        Flags = 0,
                        pName = paperName
                    };
                    formInfo.Size.width = Convert.ToInt32((width * 100));
                    formInfo.Size.height = Convert.ToInt32((height * 100));
                    formInfo.ImageableArea.left = 0;
                    formInfo.ImageableArea.right = formInfo.Size.width;
                    formInfo.ImageableArea.top = 0;
                    formInfo.ImageableArea.bottom = formInfo.Size.height;
                    if (!AddForm(hPrinter, 1, ref formInfo))
                    {
                        throw new Exception(string.Format("添加纸张{0}时发生错误，系统错误信息：{1}", paperName, GetLastError()));
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Format("添加纸张{0}时发生错误", paperName, GetLastError()), ex);
                }
                finally
                {
                    ClosePrinter(hPrinter);
                }
            }
            else
            {
                StructDevMode devMode = new StructDevMode();
                IntPtr hDC = CreateDC(null, printerName, null, ref devMode);
                if ((hDC != IntPtr.Zero))
                {
                    devMode.dmFields = 14;
                    devMode.dmPaperSize = 0x100;
                    devMode.dmPaperWidth = Convert.ToInt16(((width * 2.54) * 10000));
                    devMode.dmPaperLength = Convert.ToInt16(((height * 2.54) * 10000));
                    ResetDC(hDC, ref devMode);
                    DeleteDC(hDC);
                }
            }

        }
    }


}
